package com.bill.gaty.audrey.expressionarithmethique.domain;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.bill.gaty.audrey.expressionarithmethique.components.Evaluateur;
import com.bill.gaty.audrey.expressionarithmethique.components.util.SaisieUtil;

public 
class  Noeud {
	 private char type= ' ';//le type de variable f:fonction, c:constante  v:variable * ou + ...
	 private  double valeur=0;//cas d'une constante
	 private char lettre=' ';// le caractere representant la variable :X par exemple
	 private Noeud  gauche=null;//cas d'operateur binaire ou fonction unaire
	 private Noeud  droite=null;//cas d'operateur binaire 
	 private long adresseFonction=0;// identifie une fonction :cos log ,ln ,log10 ...exp e



 
	 public Noeud(char type, double valeur, char lettre, Noeud gauche,
			Noeud droite, long adresseFonction) {
		super();
		this.type = type;
		this.valeur = valeur;
		this.lettre = lettre;
		this.gauche = gauche;
		this.droite = droite;
		this.adresseFonction = adresseFonction;
	}

	public Noeud clonNoeud(){
		 
		 return this;
	 }

	public Noeud( double valeur){
	 	this.type='c';
	 	this.valeur=valeur;
	 }

	 public Noeud( char lettre){
	 	this.type='v';
	 	this.lettre=lettre;
	 }
	 public Noeud( char op,Noeud opg,Noeud opd){ 
		 
	 	this.gauche=opg;
	 	this.droite=opd;
	 	this.type=op;
	 }
	 public int rang(){
			if(this.isFunction()) return 10;
			if(this.isVariable())return 9;
			if(this.isConstante())return 8;
			return 100;
		}
	 public Noeud( long function,Noeud gauche){//gauche argument dNoeud
		 if(gauche.isConstante()){
			 this.type='c';
			 this.valeur=(new Evaluateur()).val(new Noeud('f', -1, ' ', gauche, null, function));
		 }
		 else if(isIdentite( (function),gauche.adresseFonction)){
			Noeud tmp= gauche.getOperandeGauche();//fonction identite
			this.type = tmp.getType();
			this.valeur = tmp.getValConstante();
			this.lettre = tmp.getLettreVariable();
			this.gauche = tmp.getOperandeGauche();
			this.droite = tmp.getOperandeDroite();
			this.adresseFonction = tmp.getadresseFonction();
		 }
		 else{
	 	this.adresseFonction=function;
	 	this.type='f';
	 	this.gauche=gauche;
		 }
	 	
	 }
	 private boolean isIdentite(Long fonction, Long argument) {
		// TODO Auto-generated method stub
		 if(null==fonction||null==argument)return false;
		 String name1=SaisieUtil.get(fonction),name2=SaisieUtil.get(argument);
		 if(name1==null||name2==null)return false;
		 name1=transformName(name1);
		 name2=transformName(name2);
		 Class<Math> c=Math.class;
		 Method m1=null;
		 Method m2=null;
		 try {
			m1=c.getMethod(name1, double.class);
			m2=c.getMethod(name2, double.class);
			double tmpvl=0.5;//System.currentTimeMillis();
			double res=(double) m1.invoke(c, tmpvl);
			res=(double) m2.invoke(c, res);
			return res==tmpvl||Math.abs(res-tmpvl)<.0000001;
		} catch (NoSuchMethodException | SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	public Noeud( String function,Noeud gauche){//gauche argument de la fo
		 this(SaisieUtil.getAdresseFunction(function),gauche);
		 	 		 }
	
	 public boolean egal(Noeud d){
		 Noeud g=this;
			if(null==d)return false;//egual
			
			
			if(g.type==d.type){
				if(g.isFunction()){
					if(isSameFunction(g,d)) return g.gauche.egal(d.gauche);
					return false;
				}
				if(g.isVariable())
				{
					if(g.lettre==d.lettre)return true;
					return false;
				}
				  
				 	if(g.isConstante())
				{
					if(g.valeur==d.valeur)return true;
					return false;
				}
				
					 
				  	if(g.isOperateur())
				{
					if(g.lettre==d.lettre)return g.gauche.egal(d.gauche)&&g.droite.egal(d.droite);
					return false;
				}
				}
				return false;
			}
			
		 
	 
	 @Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		 Noeud d=(Noeud) obj;
		 return (this.egal(d)) ;
	}
	private boolean isSameFunction(Noeud g, Noeud d) {
		// TODO Auto-generated method stub
		 if(null==g||null==d)return false;
		 
		 if(!g.isFunction())return false;
		 if(!d.isFunction())return false;
		 String name1=SaisieUtil.get(g.getadresseFonction()),name2=SaisieUtil.get(d.getadresseFonction());
		 name1=transformName(name1);name2=transformName(name2);
		 return (name1.compareToIgnoreCase(name2)==0) ;
		 
		 
		 
	}
	private boolean isSameFunction(Long g, Noeud d) {
		// TODO Auto-generated method stub
		 if(null==g||null==d)return false;
		 
		  
		 if(!d.isFunction())return false;
		 String name1=SaisieUtil.get(g),name2=SaisieUtil.get(d.getadresseFonction());
		 name1=transformName(name1);name2=transformName(name2);
		 return (name1.compareToIgnoreCase(name2)==0) ;
		 
		 
		 
	}
	private boolean isSameFunction(Noeud g, String d) {
		// TODO Auto-generated method stub
		 if(null==g||null==d)return false;
		 
		  
		 if(!g.isFunction())return false;
		 String name1=SaisieUtil.get(g.getadresseFonction()),name2=d;
		 name1=transformName(name1);name2=transformName(name2);
		 return (name1.compareToIgnoreCase(name2)==0) ;
		 
		 
		 
	}
	private boolean isSameFunction(Long g, Long d) {
		// TODO Auto-generated method stub
		 if(null==g||null==d)return false;
		 
		  
		  
		 String name1=SaisieUtil.get(g),name2=SaisieUtil.get(d );
		 name1=transformName(name1);name2=transformName(name2);
		 return (name1.compareToIgnoreCase(name2)==0) ;
		 
		 
		 
	}
	private boolean isSameFunction(Long g, String d) {
		// TODO Auto-generated method stub
		 if(null==g||null==d)return false;
		 
		  
		  
		 String name1=SaisieUtil.get(g),name2=d;
		 name1=transformName(name1);name2=transformName(name2);
		 return (name1.compareToIgnoreCase(name2)==0) ;
		 
		 
		 
	}
	private boolean isSameFunction(String g, String d) {
		// TODO Auto-generated method stub
		 if(null==g||null==d)return false;
		 
		 String name1=transformName(g),name2=transformName(d);
		 return (name1.compareToIgnoreCase(name2)==0) ;
		 
		 
		 
	}
	private String transformName(String name1) {
		// TODO Auto-generated method stub
		if("e"==name1)return "exp";
		if("ln"==name1)return "log";
		if("racinecarree"==name1)return "sqrt";
		return name1;
	}
	public static Noeud plus(Noeud fg,Noeud fd){
	 	
		if(null==fd||null==fd)return null;
		if(fg.isConstante()&&fd.isConstante())return new Noeud(fg.getValConstante()+fd.getValConstante());
	if(fg.egal(fd))return fois (new Noeud(2),fg);
	if(fg.isZero()) return fd;
	if(fd.isZero()) return fg;
	if(fd.isConstante())
		return new Noeud('+', fd, fg);
	if(fd.isFonctionMoins())return   new Noeud('-', fg, fd.getOperandeGauche());
       return new Noeud('+', fg, fd);
	 }
	 public static Noeud moins(Noeud fg,Noeud fd){
	 	// TODO Auto-generated constructor stub
		 if(null==fd||null==fd)return new Noeud(0) ;
			if(fg.isConstante()&&fd.isConstante())return new Noeud(fg.getValConstante()-fd.getValConstante());
		if(fg.egal(fd))return  new Noeud(0) ;
		if(fg.isZero()) return new Noeud("-",fd);
		if(fd.isZero()) return fg;
		if(fd.isConstante())
			  return new Noeud('+', new Noeud("-",new Noeud(fd.getValConstante())), fg);
		if(fd.isFonctionMoins())return   new Noeud('+', fg, fd.getOperandeGauche());
	       return new Noeud('-', fg, fd);
	 }
	 
	 public boolean isZero() {
		// TODO Auto-generated method stub
		return isConstante()&&valeur==0;
	}

	public static Noeud fois(Noeud fg,Noeud fd){
	 	// TODO Auto-generated constructor stub
			if(null==fg||null==fd)return null;
			if(fg.isConstante()&&fd.isConstante())return new Noeud(fg.getValConstante()*fd.getValConstante());
			if(fg.egal(fd))return pow(fd,new Noeud(2));
		
		if(fd.isConstante()&&fd.getValConstante()==1){
			 
		      return fg;
		}
		
		if(fg.isConstante()&&fg.getValConstante()==1){
			 
		      return fd;
		}
		
		if(fg.isConstante()&&fg.getValConstante()==0){
			 
		      return new Noeud(0);
		}
		
		if(fd.isConstante()&&fd.getValConstante()==0){
			 
		      return new Noeud(0);
		}
		if(fd.isConstante()&&fd.getValConstante()==-1){
			 
		      return new Noeud("-",fg);
		}
		if(fg.isConstante()&&fg.getValConstante()==-1){
			 
		      return new Noeud("-",fd);
		}
		if(fd.isDiv()&&fg.isDiv()){
			if(fg.getOperandeGauche().egal(fd.getOperandeDroite()))
				return div(fd.getOperandeGauche(), fg.getOperandeDroite());
			if(fg.getOperandeDroite().egal(fd.getOperandeGauche()))
				return div(fg.getOperandeGauche(), fd.getOperandeDroite());
		}
		if(fd.isDiv()){
			if(fg.egal(fd.getOperandeDroite()))
				return fd.getOperandeGauche();
			
		}
		if( fg.isDiv()){
			if(fg.getOperandeDroite().egal(fd))
			return fg.getOperandeGauche();
		}
		 
		if(fd.isConstante())
			return (new Noeud('*',fd,fg));
	 	return (new Noeud('*',fg,fd));
	 }
	 public static  Noeud div(Noeud fg,Noeud fd){
	 	// TODO Auto-generated constructor stub
			if(null==fd||null==fd)return null;
			if(fg.isConstante()&&fd.isConstante())return new Noeud((fg.getValConstante()*1./fd.getValConstante()));
			if(fg.egal(fd))return  new Noeud(1);
		
		if(fd.isConstante()&&fd.getValConstante()==1){
			 
		      return fg;
		}
		if(fg.isConstante()&&fg.getValConstante()==0){
			 
		      return new Noeud(0);
		}
	 	return new Noeud('/',fg,fd);
	 }
	 public static  Noeud pow(Noeud fg,Noeud fd){
	 	// TODO Auto-generated constructor stub
			if(null==fd||null==fd)return null;
			if(fg.isConstante()&&fd.isConstante())return new Noeud(Math.pow(fg.getValConstante(),fd.getValConstante()));
			if(fd.isConstante()&&fd.getValConstante()==1){
			 
		      return fg;
		}
		if(fd.isConstante() &&fd.getValConstante()==0){
			 
		      return new Noeud(1);
		}
		if(fg.isConstante() &&fg.getValConstante()==0){
			 
		      return new Noeud(0);
		}
		if(fg.isConstante() &&fg.getValConstante()==1){
			 
		      return new Noeud(1);
		}
	 	return (new Noeud('^',fg,fd));
	 }
	 public boolean isFunction(){return (this.type=='f');}
	 public boolean isConstante(){return (this.type=='c');}
	 public boolean isVariable(){return (this.type=='v');}
	 public boolean isPlus(){return (this.type=='+');}
	 public boolean isMoins(){return (this.type=='-');}
	 public boolean isMul(){return (this.type=='*');}
	 public boolean isDiv(){return (this.type=='/');}
	 public boolean isPow(){return (this.type=='^');}
	 public boolean isFonctionMoins(){
		 return this.isFunction()&&this.adresseFonction==SaisieUtil.getAdresseFunction("-");
	 }
	 public boolean isSameFunction(Noeud function){return 
			
			 this.egal(function);}
	 public boolean isOperateur(){return(isPlus()||isMoins()||isMul()||isDiv());}
	 public Noeud getOperandeGauche(){return this.gauche;}
	 public Noeud getOperandeDroite(){return this.droite;}
	 public char getType(){return this.type;}
	 public double getValConstante(){return this.valeur;}
	 public char getLettreVariable(){return this.lettre;}
	 public long getadresseFonction(){ return this.adresseFonction;}
	 public void setOperandeGauche(Noeud operande_gauche){ this.gauche=operande_gauche;}
	 public void setOperandeDroite(Noeud operande_droite){ this.droite=operande_droite;}
	 public void setType(char type){ this.type=type;}
	 public void setValConstante(double val_constante){this.valeur=val_constante;}
	 public void setLettreVariable(char lettre_variable){this.lettre=lettre_variable;}
	 public void setadresseFonction(long adresseFonction){ this.adresseFonction=adresseFonction;}
	public boolean isFacteur() {
		// TODO Auto-generated method stub
		
		  return isMul()||isDiv()||isPow();
	}
	public boolean isAdditionOp() {
		// TODO Auto-generated method stub
		return isPlus()||isMoins();
	}
	public boolean isFonctionRacineCarree() {
		// TODO Auto-generated method stub
		if(this.isFunction()){
			String name=SaisieUtil.get(getadresseFonction());
			return name.compareTo("sqrt")==0||name.compareTo("racinecarree")==0;
		}
		return false;
	}

	 }


